package ChessEngine;

import java.util.LinkedList;

import boardData.Board;
import ChessEngine.moveGenerator.MoveGenerator;
import ChessEngine.moveGenerator.Exceptions.BoardException;
import Pieces.PieceValues;

/**
 * @author daniel Diese Bewertungsfunktion betrachtet außer der Anzahl und
 *         Wertigkeit der Figuren auch noch deren Position. Dazu werden
 *         sogenannte Piece-Square-Tables für die Bewertung der Position
 *         herangezogen. Außerdem wird das Brett noch nach bestimmten
 *         Formationen durchsucht,die auch mit bewertet werden. Die
 *         Wertigkkeiten für die Pice-Squaretables, als auch die Formationen und
 *         deren Wertung sind entnommen aus: Paulsen Philipp: Lernen
 *         unterschiedlicher Bewertungsfunktionen aus Schach-Spielprotokollen,
 *         Diplomarbeit 8 Juli 2009 zu finden unter:
 *         http://www.ke.tu-darmstadt.de/bibtex/attachments/single/145 (Stand
 *         31.07.2011)
 */

public class PieceSquareTableEvaluator extends SimpleEvaluator {

	// malus
	/*
	 * Befinden sich zwei Bauern einer Seite in derselben Linie behindern sie
	 * sich gegenseitig in ihrem Bewegungsspielraum und können einander keine
	 * Deckung geben. Daher wird eine solche Situation mit Punktabzug bestraft.
	 */
	final static int DOUBLE_PAWN = 10;

	/*
	 * Befindet sich auf keiner der benachbarten Linie ein weiterer Bauer der
	 * eigenen Seite, welcher Deckung bieten könnte, so führt dies ebenfalls zu
	 * einem Abzug
	 */
	final static int ISOLATED_PAWN = 20;
	/*
	 * Befindet sich kein Bauer hinter den Bauern auf den benachbarten Linien
	 * und kann somit nicht von diesen gedeckt werden so führt dies ebenfalls zu
	 * einem Malus bzw. Er steht hinter oder neben seinen Nachbarbauern. Das hat
	 * zur Folge, dass er nicht mehr durch andere Bauern geschützt werden kann.
	 * Auf der gleichen Linie steht kein gegnerischer Bauer. Die gegnerischen
	 * Schwerfiguren können den rückständigen Bauern also leicht von vorn
	 * angreifen.
	 */
	final static int BACKWARD_PAWN = 8;

	// bonus
	/*
	 * Hat ein Bauer weder auf der eigenen noch auf den beiden benachbarten
	 * Linien einen Bauern des Gegners vor sich und kann so nur noch durch den
	 * Einsatz von Figuren geschlagen werden, so wird dieser Vorteil mit einem
	 * Bonus belohnt
	 */
	final static int PASSED_PAWN = 20;
	/*
	 * Offene Linie: Wird ein Turm durch keinen Bauern auf seiner aktuellen
	 * Linie aufgehalten und kann sich frei Bewegen wird dies ebenfalls belohnt.
	 */
	final static int OPEN_LINE = 15;

	/*
	 * Wird ein Turm nicht mehr durch einen eigenen Bauern auf der aktuellen
	 * Linie behindert so fällt der Bonus etwas geringer aus. Durch das schlagen
	 * des gegnerischen Bauern kann aber schnell eine offene Linie geschaffen
	 * werden.
	 */
	final static int HALF_OPEN_LINE = 10;

	/*
	 * Turm auf 7ter Reihe: Hat ein Turm die zweite Reihe des Gegners, die 7te
	 * Reihe aus der eigenen Sicht, erreicht wird dies ebenfalls mit
	 * Bonuspunkten belohnt, da er von dieser Position aus gegnerische Bauern
	 * bedrohen kann die noch nicht gezogen wurden und in der Regel den Koenig
	 * in seinem Bewegungsspielraum stark einschraenkt.
	 */
	final static int ROOK_AT_7 = 20;
	
	/*
	 *  TODO: Wert für gezogene Rochade überprüfen!!!
	 */
	final static int ROCHADE_DONE = 15;
	
	/*
	 *  Schlechter Wert fuer bedrohtes Feld
	 */
	final static int THREATEN_FIELD = 50;
	
	/*
	 * Koenig ist blockiert
	 */
	final static int KING_BLOCKED = 20;	
	
	/*
	 * Gegnerischer Koenig steht im Schach
	 */
	final static int ENEMY_KING_CHECKED = 50;
	
	/*
	 * Bishop hat freie Linien
	 * Bishop ist auf der Hauptdiagonalen ==> groesster Spielraum
	 */
	final static int BISHOP_HAS_FREE_LINES = 20;
	final static int BISHOP_ON_MAIN_DIAGONAL = 20;
	
	/*
	 * Schlechte Bewertung damit das aktuelle Brett nicht weiter betrachtet wird
	 */
	final static int WORST_WHITE_EVALUATION = -9999;
	final static int WORST_BLACK_EVALUATION = WORST_WHITE_EVALUATION*(-1);		
	
	public PieceSquareTableEvaluator(){
	}
	
	public int evaluateOnlyPieces(Board b) {
		int evaluation = super.evaluate(b);
		// Piece-Square Values des aktuellen Boardes
		evaluation += pieceSquareMaterialCount(b,evaluation);	
		return evaluation;
	}

	/**
	 * Fuehrt die Bewertung fuer ein Board aus, und
	 * gibt den Wert der Bewertung zurueck. 
	 * 
	 * @param b Board das bewertet werden soll.
	 */
	@Override
	public int evaluate(Board b) {
		int evaluation=0;
		// Pruefen ob eine Situation eintreffen koennte die zu einem Remis fuehren wuerde
		if (abortWithWorstEvaluation(b)) {
			if (b.isSideWhite())
				return WORST_WHITE_EVALUATION;
			else
				return WORST_BLACK_EVALUATION;
		}			
		
		// Rochade wurde durchgefuehrt -> bessere Bewertung fuer die andere Seite
		if (rochadeDone(b)) {
			if (b.isSideWhite()) {
				evaluation -= ROCHADE_DONE;				
			} else {
				evaluation += ROCHADE_DONE;
			}							
		}	
		
		// Bedrohtes Feld, Zug bekommt schlechte Bewertung
				if (evaluateThreatenField(b)) {
					int figure = b.getPiece(b.toField(b.getCurrMove().substring(2,4)));
					if (b.isSideWhite()) {			
						switch (figure) {
						case PieceValues.WHITE_PAWN:
							evaluation -= (PAWN_SCORE/100)*75;	
							break;
						case PieceValues.WHITE_ROOK:
							evaluation -= (ROOK_SCORE/100)*75;	
							break;
						case PieceValues.WHITE_KNIGHT:
							evaluation -= (KNIGHT_SCORE/100)*75;	
							break;
						case PieceValues.WHITE_BISHOP:
							evaluation -= (BISHOP_SCORE/100)*75;	
							break;
						case PieceValues.WHITE_QUEEN:
							evaluation -= (QUEEN_SCORE/100)*75;	
							break;
						case PieceValues.WHITE_KING:
							evaluation -= KING_BLOCKED;
							break;

						default:
							evaluation -= THREATEN_FIELD;	
							break;
						}
									
					} else {
						switch (figure) {
						case PieceValues.BLACK_PAWN:
							evaluation += (PAWN_SCORE/100)*75;	
							break;
						case PieceValues.BLACK_ROOK:
							evaluation += (ROOK_SCORE/100)*75;	
							break;
						case PieceValues.BLACK_KNIGHT:
							evaluation += (KNIGHT_SCORE/100)*75;	
							break;
						case PieceValues.BLACK_BISHOP:
							evaluation += (BISHOP_SCORE/100)*75;	
							break;
						case PieceValues.BLACK_QUEEN:
							evaluation += (QUEEN_SCORE/100)*75;	
							break;
						case PieceValues.BLACK_KING:
							evaluation += KING_BLOCKED;
							break;

						default:
							evaluation += THREATEN_FIELD;	
							break;
						}
					}		
				}
		
		/*
		 * Steht der gegnerische Koenig im Schach, gibt es einen Bonus
		 */
		if (enemyKingInCheck(b)) {
			if (!b.isSideWhite()) {
				evaluation += ENEMY_KING_CHECKED;				
			} else {
				evaluation -= ENEMY_KING_CHECKED;
			}
		}
		
		/*
		 * Reiner Materialwert der Figuren
		 */
		int materialCount = super.evaluate(b);
		
		int enemyKingEvaluation = canEnemyKingMove(b);
		if (!b.isSideWhite()) {
			enemyKingEvaluation *= (-1);
		}
		
		/*
		 * Stellungsbewertung des Boardes
		 */
		evaluation = materialCount
					+ calculateDoublePawnFactor(b) + calculateIsolatedPawnFactor(b)
		 			+ calculateBackwardPawnFactor(b) + calculatePassedPawnFactor(b)
		 			+ calculateOpenLineFactor(b) + calculateHalfOpenLineFactor(b)
		 			+ calculateRookAt7Factor(b) + enemyKingEvaluation; 
		
		// Piece-Square Values des aktuellen Boardes
		evaluation += pieceSquareMaterialCount(b,materialCount);		
		
		return evaluation;
	}			

	/** 
	 * Stellung erscheint zum 2. Mal, ein drittes Mal muss verhindert werden,
	 * da dies zu einem Remis führen würde -> Schlechte Bewertung!!!
	 * 
	 * Folgendes wird geprueft:
	 * Stellung zum 3. Mal
	 * 50-Züge-Regel
	 * Patt
	 * @param b Board fuer welches auf Patt / 3-Mal gleiche Stellung / 50 Zuege-Regel geprueft werden soll
	 */
	private boolean abortWithWorstEvaluation(Board b) {		
		int boardHistoryCount=b.getHistory().matches(b);
		
		if ((boardHistoryCount == 3) || (b.getHistory().getHistoryCount() == 100) || 
			(isPatt(b)))  {			
				return true;
		} else {
			return false;				
		}	
	}
	
	private int calculateRookAt7Factor(Board b) {
		final int whiteRookAt7 = calculateWhiteRookAt7(b);
		final int blackRoockAt7 = calculateBlackRookAt7(b);

		return whiteRookAt7 - blackRoockAt7;
	}

	private int calculateBlackRookAt7(Board b) {
		for (int i = 81; i <= 89; i++) {
			if (b.getPiece(i) == Pieces.PieceValues.BLACK_ROOK) {
				return ROOK_AT_7;
			}
		}
		return 0;
	}

	private int calculateWhiteRookAt7(Board b) {
		for (int i = 31; i <= 39; i++) {
			if (b.getPiece(i) == Pieces.PieceValues.WHITE_ROOK) {
				return ROOK_AT_7;
			}
		}
		return 0;
	}

	private int calculateHalfOpenLineFactor(Board b) {
		return whiteHalfOpenLine(b) - blackHalfOpenLine(b);
	}

	private int whiteHalfOpenLine(Board b) {
		for (int i = 98; i <= 21; i--) {
			if (b.getPiece(i) == Pieces.PieceValues.WHITE_ROOK) {
				final int line = i % 10;
				for (int j = i; j >= 21; j -= 10) {
					final int piece = b.getPiece(j);
					if (piece == Pieces.PieceValues.WHITE_BISHOP
							|| piece == Pieces.PieceValues.WHITE_KING
							|| piece == Pieces.PieceValues.WHITE_KNIGHT
							|| piece == Pieces.PieceValues.WHITE_PAWN
							|| piece == Pieces.PieceValues.WHITE_QUEEN
							|| piece == Pieces.PieceValues.WHITE_ROOK) {
						return 0;
					}
				}

				for (int j = i; j <= 98; j += 10) {
					final int piece = b.getPiece(j);
					if (piece == Pieces.PieceValues.WHITE_BISHOP
							|| piece == Pieces.PieceValues.WHITE_KING
							|| piece == Pieces.PieceValues.WHITE_KNIGHT
							|| piece == Pieces.PieceValues.WHITE_PAWN
							|| piece == Pieces.PieceValues.WHITE_QUEEN
							|| piece == Pieces.PieceValues.WHITE_ROOK) {
						return 0;
					}
				}
			}
		}
		return HALF_OPEN_LINE;
	}

	private int blackHalfOpenLine(Board b) {
		for (int i = 21; i <= 98; i++) {
			if (b.getPiece(i) == Pieces.PieceValues.BLACK_ROOK) {
				final int line = i % 10;
				for (int j = i; j >= 21; j -= 10) {
					final int piece = b.getPiece(j);
					if (piece == Pieces.PieceValues.BLACK_BISHOP
							|| piece == Pieces.PieceValues.BLACK_KING
							|| piece == Pieces.PieceValues.BLACK_KNIGHT
							|| piece == Pieces.PieceValues.BLACK_PAWN
							|| piece == Pieces.PieceValues.BLACK_QUEEN
							|| piece == Pieces.PieceValues.BLACK_ROOK) {
						return 0;
					}
				}

				for (int j = i; j <= 98; j += 10) {
					final int piece = b.getPiece(j);
					if (piece == Pieces.PieceValues.BLACK_BISHOP
							|| piece == Pieces.PieceValues.BLACK_KING
							|| piece == Pieces.PieceValues.BLACK_KNIGHT
							|| piece == Pieces.PieceValues.BLACK_PAWN
							|| piece == Pieces.PieceValues.BLACK_QUEEN
							|| piece == Pieces.PieceValues.BLACK_ROOK) {
						return 0;
					}
				}
			}
		}
		return HALF_OPEN_LINE;
	}

	private int calculateOpenLineFactor(Board b) {

		return whiteOpenLineFactor(b) - blackOpenLineFactor(b);
	}

	private int whiteOpenLineFactor(Board b) {
		for (int i = 98; i <= 21; i--) {
			if (b.getPiece(i) == Pieces.PieceValues.WHITE_ROOK) {
				final int line = i % 10;
				boolean openLine = true;
				for (int j = line + 20; i <= 98; j += 10) {
					if (b.getPiece(j) != Pieces.PieceValues.EMPTY_FIELD) {
						openLine = false;
					}
				}
				if (openLine) {
					return OPEN_LINE;
				}
			}
		}
		return 0;
	}

	private int blackOpenLineFactor(Board b) {
		for (int i = 21; i <= 98; i++) {
			if (b.getPiece(i) == Pieces.PieceValues.BLACK_ROOK) {
				final int line = i % 10;
				boolean openLine = true;
				for (int j = line + 20; j <= 98; j += 10) {
					if (b.getPiece(j) != Pieces.PieceValues.EMPTY_FIELD) {
						openLine = false;
					}
				}
				if (openLine) {
					return -OPEN_LINE;
				}
			}
		}
		return 0;
	}

	private int calculatePassedPawnFactor(Board b) {
		return whitePassedPawn(b) - blackPassedPawn(b);
	}

	private int blackPassedPawn(Board b) {
		int result = 0;
		for (int i = 31; i <= 98; i++) {
			final int piece = b.getPiece(i);
			if (piece == Pieces.PieceValues.BLACK_PAWN) {
				// Eigene linie durch gegnerbauer blockiert
				boolean ownLineBlocked = false;
				for (int j = i % 10 + 30; j <= 98; j += 10) {
					if (b.getPiece(j) == Pieces.PieceValues.WHITE_PAWN) {
						ownLineBlocked = true;
						break;
					}
				}
				// linke linie durch gegnerbauer blockiert
				boolean leftLineBlocked = false;
				if (!ownLineBlocked
						&& b.getPiece(i % 10 - 1) != Pieces.PieceValues.ILLEGAL_FIELD) {
					for (int j = i % 10 + 30 - 1; i <= 98; i += 10) {
						if (b.getPiece(j) == Pieces.PieceValues.WHITE_PAWN) {
							leftLineBlocked = true;
							break;
						}
					}
				}

				// linke line durch gegnerbauer blockiert
				boolean rightLineBlocked = false;
				if (!ownLineBlocked && !leftLineBlocked
						&& b.getPiece(i % 10 + 1) != Pieces.PieceValues.ILLEGAL_FIELD) {
					for (int j = i % 10 + 30 + 1; i <= 98; i += 10) {
						if (b.getPiece(j) == Pieces.PieceValues.WHITE_PAWN) {
							leftLineBlocked = true;
							break;
						}
					}
				}

				if (!ownLineBlocked && !leftLineBlocked && !rightLineBlocked) {
					result -= PASSED_PAWN;
				}
			}
		}
		return result;
	}

	private int whitePassedPawn(Board b) {
		int result = 0;
		for (int i = 98; i >= 31; i--) {
			final int piece = b.getPiece(i);
			if (piece == Pieces.PieceValues.WHITE_PAWN) {
				// Eigene linie durch gegnerbauer blockiert
				boolean ownLineBlocked = false;
				for (int j = i % 10 + 30; j >= 21; j -= 10) {
					if (b.getPiece(j) == Pieces.PieceValues.BLACK_PAWN) {
						ownLineBlocked = true;
						break;
					}
				}
				// linke linie durch gegnerbauer blockiert
				boolean leftLineBlocked = false;
				if (!ownLineBlocked
						&& b.getPiece(i % 10 - 1) != Pieces.PieceValues.ILLEGAL_FIELD) {
					for (int j = i % 10 + 30 - 1; i >= 21; i -= 10) {
						if (b.getPiece(j) == Pieces.PieceValues.BLACK_PAWN) {
							leftLineBlocked = true;
							break;
						}
					}
				}

				// linke line durch gegnerbauer blockiert
				boolean rightLineBlocked = false;
				if (!ownLineBlocked && !leftLineBlocked
						&& b.getPiece(i % 10 + 1) != Pieces.PieceValues.ILLEGAL_FIELD) {
					for (int j = i % 10 + 30 + 1; i >= 31; i -= 10) {
						if (b.getPiece(j) == Pieces.PieceValues.BLACK_PAWN) {
							rightLineBlocked = true;
							break;
						}
					}
				}
				if (!ownLineBlocked && !leftLineBlocked && !rightLineBlocked) {
					result += PASSED_PAWN;
				}
			}
		}
		return result;
	}

	private int calculateBackwardPawnFactor(Board b) {
		return whiteBackwardPawn(b) - blackBackwardPawn(b);
	}

	private int blackBackwardPawn(Board b) {
		int result = 0;
		for (int i = 31; i <= 98; i++) {
			final int piece = b.getPiece(i);
			if (piece == Pieces.PieceValues.BLACK_PAWN) {
				if (b.getPiece(i - 11) == Pieces.PieceValues.BLACK_PAWN
						|| b.getPiece(i + 11) == Pieces.PieceValues.BLACK_PAWN) {
					continue;
				}
				boolean openLine = true;
				for (int j = i % 10 + 30; j <= 98; j += 10) {
					if (b.getPiece(j) == Pieces.PieceValues.EMPTY_FIELD) {
						continue;
					} else {
						openLine = false;
					}
				}
				if (openLine) {
					result += BACKWARD_PAWN;
				}
			}
		}
		return result;
	}

	private int whiteBackwardPawn(Board b) {
		int result = 0;
		for (int i = 88; i >= 21; i--) {
			final int piece = b.getPiece(i);
			if (piece == Pieces.PieceValues.WHITE_PAWN) {
				if (b.getPiece(i - 11) == Pieces.PieceValues.WHITE_PAWN
						|| b.getPiece(i + 11) == Pieces.PieceValues.WHITE_PAWN) {
					continue;
				}
				boolean openLine = true;
				for (int j = i % 10 + 80; j >= 21; j -= 10) {
					if (b.getPiece(j) == Pieces.PieceValues.EMPTY_FIELD) {
						continue;
					} else {
						openLine = false;
					}
				}
				if (openLine) {
					result += -BACKWARD_PAWN;
				}
			}
		}
		return result;
	}

	private int calculateIsolatedPawnFactor(Board b) {
		return whiteIsolatedPawn(b) - blackIsolatedPawn(b);
	}

	private int blackIsolatedPawn(Board b) {
		int result = 0;
		for (int i = 31; i <= 98; i++) {
			final int piece = b.getPiece(i);
			if (piece == Pieces.PieceValues.BLACK_PAWN) {
				if (b.getPiece(i - 1) == Pieces.PieceValues.BLACK_PAWN
						|| b.getPiece(i + 1) == Pieces.PieceValues.BLACK_PAWN) {
					continue;
				} else {
					result += ISOLATED_PAWN;
				}
			}
		}
		return result;
	}

	private int whiteIsolatedPawn(Board b) {
		int result = 0;
		for (int i = 31; i <= 98; i++) {
			final int piece = b.getPiece(i);
			if (piece == Pieces.PieceValues.WHITE_PAWN) {
				if (b.getPiece(i - 1) == Pieces.PieceValues.WHITE_PAWN
						|| b.getPiece(i + 1) == Pieces.PieceValues.WHITE_PAWN) {
					continue;
				} else {
					result += -ISOLATED_PAWN;
				}
			}
		}
		return result;
	}

	private int calculateDoublePawnFactor(Board b) {
		return whiteDoublePawn(b) - blackDoublePawn(b);
	}

	private int blackDoublePawn(Board b) {
		int result = 0;
		for (int i = 31; i <= 98; i++) {
			final int piece = b.getPiece(i);
			if (piece == Pieces.PieceValues.BLACK_PAWN) {
				for (int j = i; j <= 98; j += 10) {
					if (b.getPiece(j) == Pieces.PieceValues.BLACK_PAWN) {
						result += DOUBLE_PAWN;
					}
				}
			}
		}
		return result;
	}

	private int whiteDoublePawn(Board b) {
		int result = 0;
		for (int i = 88; i >= 21; i-=10) {
			final int piece = b.getPiece(i);
			if (piece == Pieces.PieceValues.WHITE_PAWN) {
				for (int j = i; j >= 21; j -= 10) {
					if (b.getPiece(j) == Pieces.PieceValues.WHITE_PAWN) {
						result += -DOUBLE_PAWN;
					}
				}
			}
		}
		return result;
	}	
	
	/**
	 * Die Funtion prüft ob eine Rochade gezogen wurde
	 * Der König muss um mehr als 2 Felder gezogen worden sein
	 * Konkret kann der König nach der Rochade nur je nach Farbe nur auf zwei speziellen
	 * Feldern stehen, diese werden abgefragt, anhand von dem als letzten Zuges 
	 * @param b Board welches auf gezogene Rochade geprueft werden soll.
	 */
	private boolean rochadeDone(Board b) {
		// Rochade weiss
		final String shortRochWhite = "e1g1";
		final String longRochWhite  = "e1c1";
		// Rochade schwarz
		final String shortRochBlack = "e8g8";
		final String longRochBlack  = "e8c8";
		
		boolean rochDone = false;
		
			String lastMove = b.getCurrMove();
			if (lastMove != null) {	
			if (b.isSideWhite()) { // vorher war schwarz am Zug	
			
				if (lastMove.equals(shortRochBlack)) {	
					rochDone = true;
				} else if (lastMove.equals(longRochBlack)) {
					rochDone = true;
				}
				
			}else {
					if (lastMove.equals(shortRochWhite)) {
						rochDone = true;
					} else if (lastMove.equals(longRochWhite)) {
						rochDone = true;
					}		
				}					
		}
						
		return rochDone;
	}
	
	/**
	 * Patt liegt vor wenn der Koenig keinen gueltigen Zug mehr
	 * ausfuehren kann.
	 * 
	 * @param b Board das geprueft werden soll, ob eine Rochade gezogen worden ist.
	 */
	private boolean isPatt(Board b) {
		MoveGenerator mg = new MoveGenerator();
		LinkedList<Board> list = null;
		
		try {
			list = mg.generateMove(b, b.isSideWhite());
		}
		catch(BoardException e) {			
		}
		
		if (list.isEmpty() || (list == null)) {
			return true;
		}
		
		return false;
	}
	
	/**
	 * Liefert true wenn das Feld, auf das aktuell gezogen wurde bedroht ist.
	 * 
	 * @param b aktuelles Board, das geprueft werden soll
	 * @return true, wenn das Zielfeld bedroht wird, false wenn es nicht bedroht wird!
	 */
	private boolean evaluateThreatenField(Board b) {
		String targetField = b.getCurrMove().substring(2, 4);
		MoveGenerator mg = new MoveGenerator();
		boolean treaten = mg.isThreatens(b,b.toField(targetField),b.isSideWhite());	
		
		return treaten;
	}		
	
	/**
	 * PieceSquare-Material-Count der fuer die Positionen einer Figur auf dem Brett
	 * eine spezielle Bewertung zurueckliefert.
	 * @param b aktuelles Board, fuer welches die Positionen ausgewertet werden sollen
	 * @param materialCount anhand des Materialcounts wird bestimmt, ob fuer den Koenig die normale, oder 
	 *                      die Endspieltabelle verwendet wird.
	 * @return PieceSquare-Material-Count der fuer das aktuelle Board.
	 */
	private int pieceSquareMaterialCount(Board b,int materialCount) {																		
		/*
		 *  Wert der aktuellen Evaluation wird in sum gespeichert, Werte aus der 
		 *  Piece-Square-Matrix werden hinzuaddiert, oder subtrahiert.
		 */		
		int sum=0;								
		
		PieceSquareTables pcq = new PieceSquareTables();
		for(int i=21,sub=21;i<=98;i++){
			/*
			 *  Felder die nicht im gültigen Bereich sind überspringen!
			 *  Bsp: 29,30; 39,40; 49,50...
			 *  
			 *  sub rechnet die 21 bis 98er Werte auf 0-63 Array-Indizes runter, da
			 *  die Bewertungsmatrizen als int[8x8] gespeichert sind!!!
			 */
			
			if (((i)%10)==0)
				sub+=2;
			
			if ((((i+1)%10)==0) || (((i)%10)==0))
				continue;
			
			int diff=0;
			
			switch(b.getPiece(i)){						
				// Black
				case Pieces.PieceValues.BLACK_BISHOP:
					// Stimmt sub?? PRÜFEN!!!! Sollte funktionieren. Im Einzel-Arraytest gehts :-)
					sum += pcq.getBlackBishopEvaluation(i-sub);
					if (bishopsHasFreeLines(b,i)) {
						sum -= BISHOP_HAS_FREE_LINES;
						if (bishopOnMainDiagonal(b,i))
							sum -= BISHOP_ON_MAIN_DIAGONAL;
					}					
				break;									
					
				case Pieces.PieceValues.BLACK_KNIGHT:
					sum += pcq.getBlackKnightEvaluation(i-sub);
				break;
				
				case Pieces.PieceValues.BLACK_PAWN:
					sum += pcq.getBlackPawnEvaluation(i-sub);
				break;
					
				case Pieces.PieceValues.BLACK_QUEEN:
					sum += pcq.getBlackQueenEvaluation(i-sub);
				break;
					
				case Pieces.PieceValues.BLACK_ROOK:		
					sum += pcq.getBlackRookEvaluation(i-sub);
				break;
				
				case Pieces.PieceValues.BLACK_KING:
					diff = super.countWhiteScore(b);
					// Umstellen auf Endspiel-Koenig-Bewertung
					// Damit alles richtig erkannt wird, muss vom schwarzen Wert noch der weisse abgezogen werden!
					if (isEndGame(materialCount-diff,b))
						sum += pcq.getBlackKingEndGameEvaluation(i-sub);
					else
						sum += pcq.getBlackKingMiddleGameEvaluation(i-sub);
				break;
				
				////////////////////////////////////////////////////////////////////////////
				
				// White
				case Pieces.PieceValues.WHITE_BISHOP:
					sum += pcq.getWhiteBishopEvaluation(i-sub);
					if (bishopsHasFreeLines(b,i)) {
						sum += BISHOP_HAS_FREE_LINES;
						if (bishopOnMainDiagonal(b,i))
							sum += BISHOP_ON_MAIN_DIAGONAL;
					}
				break;								
				
				case Pieces.PieceValues.WHITE_KNIGHT:
					sum += pcq.getWhiteKnightEvaluation(i-sub);
				break;
					
				case Pieces.PieceValues.WHITE_PAWN:
					sum += pcq.getWhitePawnEvaluation(i-sub);
				break;
					
				case Pieces.PieceValues.WHITE_QUEEN:
					sum += pcq.getWhiteQueenEvaluation(i-sub);
				break;
					
				case Pieces.PieceValues.WHITE_ROOK:
					sum += pcq.getWhiteRookEvaluation(i-sub);
				break;
				
				case Pieces.PieceValues.WHITE_KING:
					diff = super.countBlackScore(b);
					// Umstellen auf Endspiel-Koenig-Bewertung?
					// Damit alles richtig erkannt wird, muss vom weissen Wert noch der schwarze abgezogen werden!
					if (isEndGame(materialCount-diff,b))
						sum += pcq.getWhiteKingEndGameEvaluation(i-sub);
					else
						sum += pcq.getWhiteKingMiddleGameEvaluation(i-sub);
				break;
			}
		}
		return sum;
	}

	private boolean isEndGame(int sum, Board b) {
		boolean endGame = false;
		int matCount =0;
		if(!b.isSideWhite()){
			matCount = super.getWhiteMatCount()-AbstractEvalutor.KING_SCORE;
		}else{
			matCount = (super.getBlackMatCount()+AbstractEvalutor.KING_SCORE)*-1;
		}
		if (matCount != 0 && matCount <= 2000)
			endGame = true;		
		else
			endGame = false;		
		
		return endGame;
	}
	
	private boolean enemyKingInCheck(Board b) {
		MoveGenerator mg = new MoveGenerator();
		int enemyKingPos = mg.whereIsTheKing(b,b.isSideWhite());		
		/*
		 *  Wenn der König bedroht wird, bekommt er Panik, und sollte sich verziehen - 
		 *  wenn er kann. Wenn nicht "IL VIVRE LA REVOLUTION"
		 */
		if (mg.isThreatens(b,enemyKingPos,b.isSideWhite())) 
			return true;
		else
			return false;				
	}
	
	private int canEnemyKingMove(Board b) {
		int sum = 0;
		final int UP    = -10;
		final int DOWN  = +10;
		final int LEFT  = -1;
		final int RIGHT = +1;
		
		final int UPPER_LEFT  = -11;
		final int UPPER_RIGHT = -9;
		final int DOWN_LEFT   = +9;
		final int DOWN_RIGHT  = +11;
		
		MoveGenerator mg = new MoveGenerator();
		int enemyKingPos = mg.whereIsTheKing(b,b.isSideWhite());			
		
		if (b.getPiece(enemyKingPos+LEFT) != Pieces.PieceValues.ILLEGAL_FIELD) {
			if (b.getPiece(enemyKingPos+LEFT) == Pieces.PieceValues.EMPTY_FIELD) {
				if (mg.isThreatens(b,(enemyKingPos+LEFT),b.isSideWhite())==true) {
					
					sum += THREATEN_FIELD;
					
				}
			}
			
			if (enemyKingBlocked(b,enemyKingPos+LEFT)) {
				sum += KING_BLOCKED;
			}
		}
				
		if (b.getPiece(enemyKingPos+RIGHT) != Pieces.PieceValues.ILLEGAL_FIELD) {
			if (b.getPiece(enemyKingPos+RIGHT) == Pieces.PieceValues.EMPTY_FIELD) {
				if (mg.isThreatens(b,(enemyKingPos+RIGHT),b.isSideWhite())==true) {
					
					sum += THREATEN_FIELD;
				}
			}
			
			if (enemyKingBlocked(b,enemyKingPos+RIGHT)) {
				sum += KING_BLOCKED;
			}
		}
		
		if (b.getPiece(enemyKingPos+UP) != Pieces.PieceValues.ILLEGAL_FIELD) {
			if (b.getPiece(enemyKingPos+UP) == Pieces.PieceValues.EMPTY_FIELD) {
				if (mg.isThreatens(b,(enemyKingPos+UP),!b.isSideWhite())==true) {
					
					sum += THREATEN_FIELD;
				}
			}
			
			if (enemyKingBlocked(b,enemyKingPos+UP)) {
				sum += KING_BLOCKED;
			}
		}
				
		if (b.getPiece(enemyKingPos+DOWN) != Pieces.PieceValues.ILLEGAL_FIELD) {
			if (b.getPiece(enemyKingPos+DOWN) == Pieces.PieceValues.EMPTY_FIELD) {
				if (mg.isThreatens(b,(enemyKingPos+DOWN),b.isSideWhite())==true) {
					
					sum += THREATEN_FIELD;
				}
			}
			
			if (enemyKingBlocked(b,enemyKingPos+DOWN)) {
				sum += KING_BLOCKED;
			}
		}
				
		if (b.getPiece(enemyKingPos+UPPER_LEFT) != Pieces.PieceValues.ILLEGAL_FIELD) {
			if (b.getPiece(enemyKingPos+UPPER_LEFT) == Pieces.PieceValues.EMPTY_FIELD) {
				if (mg.isThreatens(b,(enemyKingPos+UPPER_LEFT),b.isSideWhite())==true) {
					
					sum += THREATEN_FIELD;
				}
			}
			
			if (enemyKingBlocked(b,enemyKingPos+UPPER_LEFT)) {
				sum += KING_BLOCKED;
			}
		}
		
		if (b.getPiece(enemyKingPos+UPPER_RIGHT) != Pieces.PieceValues.ILLEGAL_FIELD) {
			if (b.getPiece(enemyKingPos+UPPER_RIGHT) == Pieces.PieceValues.EMPTY_FIELD) {
				if (mg.isThreatens(b,(enemyKingPos+UPPER_RIGHT),b.isSideWhite())==true) { 
					
					sum += THREATEN_FIELD;
				}
			}
			
			if (enemyKingBlocked(b,enemyKingPos+UPPER_RIGHT)) {
				sum += KING_BLOCKED;
			}
		}
						
		if (b.getPiece(enemyKingPos+DOWN_LEFT) != Pieces.PieceValues.ILLEGAL_FIELD) {
			if (b.getPiece(enemyKingPos+DOWN_LEFT) == Pieces.PieceValues.EMPTY_FIELD) {
				if (mg.isThreatens(b,(enemyKingPos+DOWN_LEFT),b.isSideWhite())==true) {
					
					sum += THREATEN_FIELD;
				}
			}
			
			if (enemyKingBlocked(b,enemyKingPos+DOWN_LEFT)) {
				sum += KING_BLOCKED;
			}
		}		
		
		if (b.getPiece(enemyKingPos+DOWN_RIGHT) != Pieces.PieceValues.ILLEGAL_FIELD) {
			if (b.getPiece(enemyKingPos+DOWN_RIGHT) == Pieces.PieceValues.EMPTY_FIELD) {
				if (mg.isThreatens(b,(enemyKingPos+DOWN_RIGHT),b.isSideWhite())==true) { 
					
					sum += THREATEN_FIELD;
				}
			}
			
			if (enemyKingBlocked(b,enemyKingPos+DOWN_RIGHT)) {
				sum += KING_BLOCKED;
			}
		}	
		
		return sum;
	}
	
	private boolean enemyKingBlocked(Board b,int pos) {
		boolean result=false;
		
		if (b.isSideWhite()) {	// beim schwarzen Koenig nach besetzten Feldern suchen
			if ((b.getPiece(pos) == Pieces.PieceValues.BLACK_BISHOP) || 
				(b.getPiece(pos) == Pieces.PieceValues.BLACK_KNIGHT) ||
				(b.getPiece(pos) == Pieces.PieceValues.BLACK_PAWN) || 
				(b.getPiece(pos) == Pieces.PieceValues.BLACK_QUEEN) ||
				(b.getPiece(pos) == Pieces.PieceValues.BLACK_ROOK)) {
					result = true;			   			
			}
		} else {
			// beim weißen Koenig nach besetzten Feldern suchen
			if ((b.getPiece(pos) == Pieces.PieceValues.BLACK_BISHOP) || 					
			    (b.getPiece(pos) == Pieces.PieceValues.BLACK_KNIGHT) ||
				(b.getPiece(pos) == Pieces.PieceValues.BLACK_PAWN) || 
				(b.getPiece(pos) == Pieces.PieceValues.BLACK_QUEEN) ||
				(b.getPiece(pos) == Pieces.PieceValues.BLACK_ROOK)) {
				   	result = true;
			}
		}
		
		return result;
	}
	
	private boolean bishopsHasFreeLines(Board b,int pos) {
		int actPos = pos;
		boolean freeLines=false;
		// nach links unten
		while(actPos <= 91) {
			actPos+=9;
			if ((b.getPiece(actPos) == Pieces.PieceValues.EMPTY_FIELD) || 
				(b.getPiece(actPos) == Pieces.PieceValues.ILLEGAL_FIELD)) {
				freeLines=true;			
			} else { 
				freeLines=false;
				return freeLines;
			}						
		}		
					
		actPos = pos;
		
		// nach rechts unten
		while(actPos <= 98) {
			actPos+=11;
			if ((b.getPiece(actPos) == Pieces.PieceValues.EMPTY_FIELD) || 
					(b.getPiece(actPos) == Pieces.PieceValues.ILLEGAL_FIELD)) {
				freeLines=true;
			} else {
				freeLines=false;

				return freeLines;
			}						
		}
		
		actPos = pos;
			
		// nach links oben
		while(actPos >= 21) {
			actPos-=11;
			if ((b.getPiece(actPos) == Pieces.PieceValues.EMPTY_FIELD)|| 
			(b.getPiece(actPos) == Pieces.PieceValues.ILLEGAL_FIELD)) {
				freeLines=true;
			} else {
				freeLines=false;
				return freeLines;
			}			
		}		
			
		// nach rechts oben
		while(actPos >= 28) {
			actPos-=9;
			if ((b.getPiece(actPos) == Pieces.PieceValues.EMPTY_FIELD) || 
			(b.getPiece(actPos) == Pieces.PieceValues.ILLEGAL_FIELD)) {
				freeLines=true;
			} else {
				freeLines=false;
				//System.out.println("actPos >= 26 pos: " + actPos);
				return freeLines;
			}			
		}
		
		return freeLines;
	}
	
	private boolean bishopOnMainDiagonal(Board b,int pos) {
		boolean result=false;
		for(int i=28,j=21; i<=91; i+=9,j+=11) {
			if (b.isSideWhite()) {
				if ((b.getPiece(i) == Pieces.PieceValues.WHITE_BISHOP) ||
					(b.getPiece(j) == Pieces.PieceValues.WHITE_BISHOP))	
					result=true;
			} else {
				if ((b.getPiece(i) == Pieces.PieceValues.BLACK_BISHOP) ||
					(b.getPiece(j) == Pieces.PieceValues.BLACK_BISHOP))
					result=true;
			}			
		}
		return result;
	}
}

